# -*- coding: utf-8 -*-
# @Author: Jerry
# @Date:   2022-02-25 08:38:28
# @Last Modified by:   Jerry
# @Last Modified time: 2022-03-01 15:30:24
# http://www.woshicver.com

# **********
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
import os
rootpath = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
datapath = os.path.join(rootpath,'data')
imgpath = lambda name: os.path.join(datapath,name)

'''
目标
在本章中，
  - 我们将学习图像金字塔
  --- 一幅图像的金字塔是一系列以金字塔形状排列的分辨率逐步降低的图像集合。
  --- 金字塔的底部是待处理图像的高分辨率表示，而顶部是低分辨率的近似。
  --- 当向金字塔的上层移动时，尺寸和分辨率就降低。
  --- 在图像处理学科里，图像金字塔被广泛的用于图像融合
  - 我们将使用图像金字塔创建一个新的水果“Orapple”
  - 我们将看到以下功能：cv.pyrUp()，cv.pyrDown()

理论
  通常，我们过去使用的是恒定大小的图像。但是在某些情况下，我们需要使用不同分辨率的（相同）图像。
  例如，当在图像中搜索某些东西（例如人脸）时，我们不确定对象将以多大的尺寸显示在图像中。
  在这种情况下，我们将需要创建一组具有不同分辨率的相同图像，并在所有图像中搜索对象。
  这些具有不同分辨率的图像集称为“图像金字塔”
  因为当它们堆叠在底部时，最高分辨率的图像位于底部，最低分辨率的图像位于顶部时，看起来像金字塔

有两种图像金字塔。
  1）高斯金字塔
    高斯金字塔中的较高级别（低分辨率）是通过删除较低级别（较高分辨率）图像中的连续行和列而形成的。
    然后，较高级别的每个像素由基础级别的5个像素的贡献与高斯权重形成。
    通过这样做，M×N图像变成M/2×N/2图像。因此面积减少到原始面积的四分之一。它称为Octave。
    当我们在金字塔中越靠上时（即分辨率下降），这种模式就会继续。
    同样，在扩展时，每个级别的面积变为4倍。
    我们可以使用 cv.pyrDown() 和 cv.pyrUp()函数找到高斯金字塔。

    cv.pyrDown(src[, dst[, dstsize[, borderType]]]) -> dst
    -- it downsamples the image by rejecting even rows and columns

    cv.pyrUp(src[, dst[, dstsize[, borderType]]]) -> dst
    -- The function performs the upsampling step of the Gaussian pyramid construction,
    though it can actually be used to construct the Laplacian pyramid.
    -- First, it upsamples the source image by injecting even zero rows and columns
    and then convolves the result with the same kernel as in pyrDown multiplied by 4.

  2）拉普拉斯金字塔
    -- 拉普拉斯金字塔由高斯金字塔形成。没有专用功能。
    拉普拉斯金字塔图像仅像边缘图像。它的大多数元素为零。它们用于图像压缩。
    拉普拉斯金字塔的层由高斯金字塔的层与高斯金字塔的高层的扩展版本之间的差形成。
'''

def pyrDown_test():
    # img = cv.imread(imgpath('messi5.jpg')) # 读出来的是个BGR 3通道原图
    # print img.shape # (342, 548, 3)
    img = cv.imread(imgpath('messi5.jpg'),0) #从BRG 3通道中抽出了一个通道
    # print img.shape # (342, 548)
    cv.imshow('img',img)
    # cv.waitKey(0)
    # cv.destroyAllWindows()
    lower1=cv.pyrDown(img)
    cv.imshow('lower1',lower1)
    lower2=cv.pyrDown(lower1)
    cv.imshow('lower2',lower2)
    lower3=cv.pyrDown(lower2)
    cv.imshow('lower3',lower3)
    cv.waitKey(0)
    cv.destroyAllWindows()


def apple_orange_RongHe():
    A = cv.imread(imgpath('apple.png'))
    B = cv.imread(imgpath('orange.png'))
    #####
    # 生成A的高斯金字塔, 用pyrDown对上一层级别的做运算
    G = A.copy()
    gpA = [G]
    for i in xrange(6):
        G = cv.pyrDown(G)
        gpA.append(G)
    # 生成A的拉普拉斯金字塔
    lpA = [gpA[5]]
    for i in xrange(5,0,-1):
        GE = cv.pyrUp(gpA[i]) # 对高斯金字塔每一层的都做pyrUp计算
        rows,cols,ch = gpA[i-1].shape
        r1,c1,ch1 = GE.shape
        r = rows if rows<=r1 else r1
        c = cols if cols<=c1 else c1
        L = cv.subtract(gpA[i-1][:r,:c],GE[:r,:c])
        # 此时gpA[i-1]和GE有相同的分辨率和尺寸，
        # 此时用 拉普拉斯金字塔数据 = 原有的高斯数据 - pyrUp计算后的数据
        lpA.append(L)
    #####
    # 生成B的高斯金字塔, 用pyrDown对上一层级别的做运算
    G = B.copy()
    gpB = [G]
    for i in xrange(6):
        G = cv.pyrDown(G)
        gpB.append(G)
    # 生成A的拉普拉斯金字塔
    lpB = [gpB[5]]
    for i in xrange(5,0,-1):
        GE = cv.pyrUp(gpB[i]) # 对高斯金字塔每一层的都做pyrUp计算
        rows,cols,ch = gpB[i-1].shape
        r1,c1,ch1 = GE.shape
        r = rows if rows<=r1 else r1
        c = cols if cols<=c1 else c1
        L = cv.subtract(gpB[i-1][:r,:c],GE[:r,:c])
        # 此时gpA[i-1]和GE有相同的分辨率和尺寸，
        # 此时用 拉普拉斯金字塔数据 = 原有的高斯数据 - pyrUp计算后的数据
        lpB.append(L)
    #####
    # 现在在每个级别中添加左右两半图像
    LS = []
    for la,lb in zip(lpA,lpB):
        rows,cols,dpt = la.shape
        ls = np.hstack((la[:,0:cols/2], lb[:,cols/2:]))
        LS.append(ls)
    #####
    # 现在重建
    ls_ret = LS[0]
    for i in xrange(1,6):
        ls_ret = cv.pyrUp(ls_ret)      # idx=0和idx=1的两个层分辨率不同，即size不同，
                                       # 所以必须先将size小的升一次，相同的情况下才能相加
        rows,cols,ch = ls_ret.shape
        r1,c1,ch1 = LS[i].shape
        r = rows if rows<=r1 else r1
        c = cols if cols<=c1 else c1
        ls_ret = cv.add(ls_ret[:r,:c], LS[i][:r,:c]) # 将所有的层都加到一起，组合为一个图，最终的ls_就是融合后的图形
    #####
    # 直接拼接各自一半的图形
    real = np.hstack((A[:,:cols/2],B[:,cols/2:]))
    cv.imshow('apple',A)
    cv.imshow('orange',B)
    cv.imshow('direct',real)
    cv.imshow('pyramid',ls_ret)
    cv.waitKey(0)
    cv.destroyAllWindows()

    # 从这个过程可知，融合的图片是对每一row的数据都进行了处理，只是这种情况，在拼接的缝隙处视觉上表现更好些而已

# 对于到目前为止所学习的这些方法，虽然努力去理解这些函数和概念，但是有一些仍旧是理解不清楚，特别是算法过程
# 即使通过函数和教程上的例子能观察到实际的一个效果
# 这些学到的都是死的东西，不知道什么时候用，该怎么用。这就是未入门而熟悉概念的一个阶段
# 距离视觉开发更是十万八千里，遥不可及



if __name__ == '__main__':
    # pyrDown_test()
    apple_orange_RongHe()










